package cn.tedu.justone.pack02;

/**
 * ZZ：JustOne
 * VX：Andylau2020smile
 * DY：JustOne2018
 * ZT：《synchronized修饰代码块》
 */
public class Demo09_synchronized修饰代码块 {


    //synchronized修饰代码块
    //synchronized修饰代码块
    //synchronized修饰代码块
    //
    //1,为了线程之间的公平(不能独占太多)，我们应该尽量缩小synchronized的修饰范围。
    //  如果一个方法中有很多代码，但只有一部份会引发线程安全问题，
    //  那么我们可以只锁会引发线程安全问题的这一部份。
    //2,用synchronized锁代码块时需要指锁对象，
    //  当多条线程访问该代码块时，只有看到是同一个锁对象的线程才会被阻塞。

    public static void main(String[] args) {
        Table09 plate = new Table09();

        Thread girl = new Thread(){
            @Override
            public void run() {
                while (plate.getMi()>0){
                    plate.eatMi();
                }
                long endTime = System.currentTimeMillis();
            }
        };

        Thread boy = new Thread(){
            @Override
            public void run() {
                while (plate.getMi()>0){
                    plate.eatMi();
                }
            }
        };

        girl.setName("女生");
        boy.setName("男生");

        girl.start();
        boy.start();

    }
}

class Table09{

    private static int mi = 10;

    public void eatMi(){
        //这个是单纯的运算，没有线程安全问题
        System.out.println(doSum());
        //这个涉及对共有数据的读、改，存在线程安全问题
        synchronized(this) {
            if (mi > 0) {
                System.out.println(Thread.currentThread().getName() + "在吃第" + mi + "颗");
                mi--;
                System.out.println("余数是：" + mi);
            } else {
                System.out.println("已售罄");
            }
        }
    }

    public int getMi(){
        return mi;
    }

    //为了代码更优雅，编写统一的随机数相加1000W次
    public int doSum(){
        int sum = 0;
        //执行1000W次的随机数相加
        for (int i = 0; i < 10000000; i++) {
            int ran = (int)(Math.random()*10);
            sum += ran;
        }
        return sum;
    }

}
